#include "bitmap.h"

#include "debug.h"
#include "string.h"

void bitmap_init(struct bitmap* map)
{
  memset(map->bits, 0, map->bitmap_bytes_lens);
}

bool bitmap_scan_test(struct bitmap* map, uint32_t bit_index)
{
  ASSERT(map != NULL);

  // get the index of witch byte it is
  uint32_t byte_index = bit_index / 8;
  // get the index of bit in byte
  uint32_t bit_count = bit_index % 8;

  return map->bits[byte_index] & (BITMAP_MASK << (7-bit_count));
}

int bitmap_scan(struct bitmap* map, uint32_t cnt)
{
  uint32_t free_byte_index = 0;

  // while current bit not equals 0xff(means full) and in range
  while (map->bits[free_byte_index] == 0xff &&
         free_byte_index < map->bitmap_bytes_lens)
  {
    free_byte_index++;
  }

  // dont have free btye
  if (free_byte_index == map->bitmap_bytes_lens)
  {
    return -1;
  }

  uint32_t free_bit_index_in_byte = 0;
  // current byte not full, get free_bit_index_in_byte
  while (map->bits[free_byte_index] >> (7 - free_bit_index_in_byte) &
         BITMAP_MASK)
  {
    free_bit_index_in_byte++;
  }

  uint32_t total_bits = free_byte_index * 8 + free_bit_index_in_byte;

  // apply for 1 bit resource
  if (cnt == 1)
  {
    return total_bits;
  }

  // apply for more continuous bits resource

  uint32_t continuous_free_bits_cnt = 0;

  uint32_t bits_to_scan = map->bitmap_bytes_lens * 8 - total_bits;

  for (int i = 0; i < bits_to_scan; i++)
  {
    if (!bitmap_scan_test(map, total_bits + i))
    {
      // next bit is 0
      continuous_free_bits_cnt++;
    }
    else
    {
      continuous_free_bits_cnt = 0;
      continue;
    }

    // find continuous free bits
    if (continuous_free_bits_cnt == cnt)
    {
      return total_bits + i - (cnt - 1);
    }
  }

  // fail to find continuous free bits
  return -1;
}

void bitmap_set(struct bitmap* map, uint32_t bit_index, int8_t value)
{
  ASSERT(value == 0 || value == 1);

  // Get the index of the byte containing the bit
  uint32_t byte_index = bit_index / 8;
  // Get the index of the bit within the byte
  uint32_t bit_offset = bit_index % 8;

  if (value)
  {
    // Set the bit to 1
    map->bits[byte_index] |= (BITMAP_MASK << (7 - bit_offset));
  }
  else
  {
    // Set the bit to 0
    map->bits[byte_index] &= ~(BITMAP_MASK << (7 - bit_offset));
  }
}
